package com.peng.scalatest

import java.util

/**
 * 隐式转换  (参数，方法，类）
 */
object ScalaTest9_Implicit {
  def main(args: Array[String]): Unit = {
    //通过隐式转换，  可以在不修改源码的情况下，动态添加某个类的方法！！！
    "asd".play();
    "asd".go()
    val list: util.ArrayList[String] = new java.util.ArrayList[String]
    list.add("1")
    list.add("2")
    list.add("3")
    //为iterator添加了foreach方法
    list.iterator().foreach(println)

  }

  /**
   * 当某个类型的变量调用一个它自身不存在的方法时，
   * *   scala 编译器会去寻找 implicit修饰的 入参为该变量类型的 方法  或者  implicit修饰的 类构造入参为该变量类型的 类
   * *   方法的话，然后隐式转换成为方法返回值的对象
   * *   类的话，就是隐式转换为这个类，
   * *   再去看能不能调用这个方法，如果可以，则编译不会报错，这算是完成了隐式转换的操作。
   *
   *
   * 所以，隐式转换可以有两种方式：
   * 1、这是隐式转换修饰的方法
   * 匹配时来匹配入参的类型
   *
   * @param string
   * @return
   */
  implicit def xxx(string: String): ImplicitClass = {
    new ImplicitClass
  }

  /**
   * 2、这是隐式转换修饰的类
   * 需要指定类的构造参数  匹配会来匹配它的参数类型
   *
   * @param string
   */
  implicit class InnerImplicitClass(string: String) {
    def go(): Unit = {
      println("go")
    }
  }

  /**
   * 这也是一个简单的实例应用    为java中的Iterator 添加 通用的foreach方法
   *
   * @param iterator
   * @tparam T
   */
  implicit class iteratorClass[T](iterator: util.Iterator[T]) {
    def foreach(f: T => Unit): Unit = {
      while (iterator.hasNext) {
        f(iterator.next())
      }

    }
  }


  /**
   * 参数 implicit 修饰的应用
   * implicit会修饰方法里的所有的参数
   */
  def myFun2(implicit string: String, num: Int) = {
    println(string, num)
  }

  //  myFun2("haha")

  //每种类型的 implicit修饰的变量 只能有一个 否则会报错
  implicit var qqq = "xoxo"
  implicit var www = 123


  //正常来说，myFun2这是一个有参数的函数，但是我调用的时候却没有加参数，却不报错？？
  //这是因为此时，编译器会去寻找 由implicit 修饰的变量，用这个值去作为参数传参进行调用
  myFun2

  //但如果有一种场景，我只想要部分参数被implicit修饰，部分是自己指定  该怎么做呢？？
  //柯利化的应用！！
  //implicit的参数体要放在最后面
  def myFun3(num: Int)(implicit string: String) = {
    println(string, num)
  }

  //第二个参数可传可不传，  不传的话就去找implicit修饰的同类型变量
  myFun3(444)
  myFun3(444)("aha")


}

class ImplicitClass {
  def play() = {
    println("我能玩。。。。")
  }
}


//不能 继承 ScalaTest10_SealedTest.scala 里 使用sealed修饰的类
//class C extends A{
//
//}
